package com.tuzhi.filestore.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.tuzhi.base.web.helper.AjaxResultHelper;
import com.tuzhi.base.web.util.ControllerUtils;
import com.tuzhi.base.web.util.TZFileUtils;
import com.tuzhi.base.web.vo.AjaxResultVO;
import com.tuzhi.common.constants.ResultConstant;
import com.tuzhi.common.domain.ResultBO;
import com.tuzhi.common.exception.BusinessException;
import com.tuzhi.filestore.config.FileStoreConfig;
import com.tuzhi.filestore.http.BillApiClient;
import com.tuzhi.filestore.http.FileStoreOtherApi;
import com.tuzhi.filestore.model.response.Result;
import com.tuzhi.filestore.mq.producter.MsgProducter;
import com.tuzhi.filestore.service.FileUpLogService;
import com.tuzhi.filestore.utils.MultipartFileUtil;
import com.tuzhi.filestore.vo.UpFileRetVo;
import com.tuzhi.vo.bill.FileUpLogVo;
import com.tuzhi.vo.bill.OrderVo;
import com.tuzhi.vo.bill.UpFileNewOrderRetVo;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.amqp.core.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.InputStream;
import java.nio.file.Paths;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 功能说明：
 *
 * @author abu
 * @version 1.1.0
 * @date 2023/3/22
 */
@Slf4j
@RestController
@RequestMapping("/file")
public class ModelController {

    @Autowired
    FileStoreConfig fileStoreConfig;

    @Autowired
    MsgProducter msgProducter;

    @Autowired
    FileUpLogService fileService;

    @Resource
    BillApiClient billApiClient;

    /* @Resource
     TzProcessApiClient tzProcessApiClient;*/
    @Resource
    FileStoreOtherApi fileStoreOtherApi;


    /**
     * 此接口由前端，上传文件创建订单调用
     *
     * @param file      文件流信息
     * @param orderInfo 创建订单相关的信息
     * @return
     */
    @PostMapping("/newOrder")
    public AjaxResultVO newOrder(@RequestParam(name = "file", required = false) MultipartFile file, @RequestParam(name = "orderInfo", required = true) String orderInfo) {
        log.info("order :{}", orderInfo);
        log.info("file:{}", file);
        AjaxResultVO ajaxResultVO = null;
        try {
            OrderVo orderVo = JSONUtil.toBean(orderInfo, OrderVo.class, false);
            if (orderVo == null) {
                throw new BusinessException("订单信息不能为空");
            }

            if ((StringUtils.isNotBlank(orderVo.getOriginOrderId()) && !ArrayUtils.contains(new String[]{"cover", "addto", "changeProcessType"}, orderVo.getOpType()))
                    || (StringUtils.isBlank(orderVo.getOriginOrderId()) && StringUtils.isNotBlank(orderVo.getOpType()))
            ) {
                throw new BusinessException("操作类型：" + orderVo.getOpType() + "，原报价系统订单号：" + orderVo.getOriginOrderId() + "，2个参数必须同时传过来。");
            }

            if ("cover".equals(orderVo.getOpType()) || "addto".equals(orderVo.getOpType())) {
                /**
                 * cover:覆盖
                 * addto:追加
                 * 一、非直落单：
                 *  旧单取消，以新的上传
                 * 二、直落单
                 * a.能上传就上传，
                 * b.不能上传待待通知看做什么操作？？？
                 *
                 * 则不管怎么样，先创建一个新的报价单
                 *
                 */
                if (StringUtils.isBlank(orderVo.getOriginOrderId())) {
                    throw new BusinessException(String.format("操作类型：%s，没有传旧文档模型产生的报价单号过来。", orderVo.getOpType()));
                }
                /*if ("addto".equals(orderVo.getOpType())) {
                    ajaxResultVO = tzProcessApiClient.updateProcessDataDelete(orderVo.getOriginOrderId(), "追加操作");
                    return ajaxResultVO;
                }
                if ("cover".equals(orderVo.getOpType())) {
                    //cover覆盖操作类型的生成一个新报价单号，并且把新的报价单号设置给旧报价单号，把Erp的框数据置为无效，----继续往下走就可以了
                    ajaxResultVO = tzProcessApiClient.updateDataDelete4Frame(orderVo.getOriginOrderId(), "覆盖操作");
                    log.info("旧单号={}，覆盖操作类型，将Erp框数据置为无效结果：{}", orderVo.getOriginOrderId(), ajaxResultVO);
                }*/
            }
            if ("changeProcessType".equals(orderVo.getOpType())) {
                //更改加工类型，重报价，拿回旧文档
                com.tuzhi.base.web.vo.Result<Map> billHeaderMap = billApiClient.getBillOrderHeaderById(Long.parseLong(orderVo.getOriginOrderId()));
                if (!"0".equals(billHeaderMap.getCode()) || billHeaderMap.getData() == null) {
                    throw new BusinessException("找不到原单号信息，" + billHeaderMap.getDescription());
                }
                Map headMap = billHeaderMap.getData();
                orderVo.setCustomerPo(headMap.get("customerPo").toString());
                orderVo.setCustomerId(Long.parseLong(headMap.get("customerId").toString()));
                //获取报价员信息
                AjaxResultVO userAjaxVo = billApiClient.getUserById(Long.parseLong(headMap.get("createUser").toString()));
                if (userAjaxVo == null || !userAjaxVo.getCode().equals("0") || userAjaxVo.getData() == null) {
                    throw new BusinessException(String.format("找不到 报价员，UserId:%s", headMap.get("createUser")));
                }
                Map userInfo = (Map) userAjaxVo.getData();
                orderVo.setCreateUser(userInfo.get("username").toString());
                //文件路径信息
                com.tuzhi.base.web.vo.Result<List<String>> result = billApiClient.getDownFilePathByOrderId("stp", Long.parseLong(headMap.get("id").toString()));
                if (result != null && result.getCode().equals("0")) {
                    // String filePath = ajaxResultVO.getData().toString();
                    List<String> paths = result.getData();
                    if (paths == null || paths.isEmpty()) {
                        throw new BusinessException("获取路径失败");
                    }
                    String filePath = Paths.get(fileStoreConfig.getSavePath(), paths.toArray(new String[0])).toString();
                    boolean fileExist = FileUtil.exist(filePath);
                    if (!fileExist) {
                        throw new BusinessException(String.format("文件不存在：%s", filePath));
                    }
                    file = MultipartFileUtil.toMultipartFile(IOUtils.toByteArray(new FileInputStream(filePath)), headMap.get("fileName").toString(), "application/octet-stream");
                } else {
                    throw new BusinessException(String.format("找不到 orderId: %s，的文件信息", headMap.get("id")));
                }
            }

            if (file == null) {
                throw new BusinessException("文件不能为空");
            }
            if (StringUtils.isEmpty(orderInfo)) {
                throw new BusinessException("订单信息不能为空");
            }

            orderVo.setFileName(file.getOriginalFilename());
           /* if (StringUtils.isEmpty(orderVo.getCreateUser())) {
                throw new BusinessException("createUser 创建人不能为空");
            }*/
            if (orderVo.getCustomerId() == null && StringUtils.isEmpty(orderVo.getCustomerCode())) {
                throw new BusinessException("customerId 客户ID，客户编码 不能同时为空");
            }
            if (StringUtils.isEmpty(orderVo.getCustomerPo())) {
                orderVo.setCustomerPo(orderVo.getFileName());
            }
            Result<FileUpLogVo> result = fileService.upLoadOrderModel(file);
            if (!result.isSuccess()) {
                ajaxResultVO = AjaxResultHelper.error();
                ajaxResultVO.setMsg("创建订单失败，原因上传文件失败：-" + result.getDescription());
                return ajaxResultVO;
            }

            String lot = DateUtil.format(new Date(), "yyyyMMddHHmmss");
            FileUpLogVo upLogEntity = result.getData();

            upLogEntity.setUpLot(lot);
            //upLogEntity.setCreatedTime(new Date());
            orderVo.setFileUpLogVo(upLogEntity);
            UpFileRetVo retVo = createOrderAndUploadLog(orderVo);
            log.info("createOrderAndUploadLog创建订单返回 ：{}", retVo != null ? new JSONObject(retVo).toString() : "返回空");
            if (retVo.getCode() == 0) {
                ajaxResultVO = AjaxResultHelper.success();
                Message message = new Message(String.format(retVo.getId().toString()).getBytes());
                String upFileType = "cadFile";
                if (upFileType.equalsIgnoreCase(retVo.getMsg())) {
                    msgProducter.sendMsg("topic_task", "parse.convert", message);
                    log.info("发送消息成功！topic_task,parse.convert{}", retVo.getId());
                } else {
                    msgProducter.sendMsg("topic_task", "parse.step", message);
                    log.info("发送消息成功！topic_task,parse.step{}", retVo.getId());
                }
            } else {
                ajaxResultVO = AjaxResultHelper.error();
            }
            ajaxResultVO.setData(retVo);
            return ajaxResultVO;

        } catch (Exception ex) {
            log.warn("上传异常，orderInfo{}-{}", orderInfo, ExceptionUtils.getRootCauseStackTrace(ex));
            ResultConstant.RC rc = new ResultConstant.RC("uploadFilessErr", ex.getMessage());
            AjaxResultVO ajax = AjaxResultHelper.build(rc);
            return ajax;
        }
    }


    /**
     * 创建订单，并保存文件（这里没有保存文件操作吧？？？） 日志信息，模型信息     *
     *
     * @param orderVo
     * @return
     */
    private UpFileRetVo createOrderAndUploadLog(OrderVo orderVo) {
        log.info("创建订单，上传日志记录：{}", orderVo.toString());
        try {
            log.info("调用BILL 创建订单：{}", ControllerUtils.getToken(null));
            com.tuzhi.base.web.vo.Result<UpFileNewOrderRetVo> resultVO = billApiClient.newOrder(JSONUtil.parse(orderVo).toString(), ControllerUtils.getToken(null));

            log.info("创建订单返回 ：{}", JSONUtil.parse(resultVO).toString());
            if (!"0".equalsIgnoreCase(resultVO.getCode())) {
                UpFileRetVo retVo = new UpFileRetVo();
                retVo.setFileName(orderVo.getFileName());
                retVo.setCode(-1);
                retVo.setMsg("创建订单失败:" + resultVO.getDescription());
                return retVo;
            }
            UpFileNewOrderRetVo upFileNewOrderRetVo = resultVO.getData();

            UpFileRetVo retVo = new UpFileRetVo();
            Long modelId = Long.parseLong(upFileNewOrderRetVo.getId().toString());
            Long orderId = upFileNewOrderRetVo.getOrderId();
            retVo.setId(modelId);
            retVo.setOrderId(orderId);
            retVo.setMsg(upFileNewOrderRetVo.getMsg());
            return retVo;
        } catch (Exception exception) {
            log.warn("创建订单，上传记录时异常:fileName:{},err:{}", orderVo.getFileUpLogVo().getOriginalFileName(), ExceptionUtils.getStackTrace(exception));
            UpFileRetVo retVo = new UpFileRetVo();
            retVo.setFileName(orderVo.getFileUpLogVo().getOriginalFileName());
            retVo.setCode(-1);
            retVo.setMsg("创建失败，保存记录时异常" + ExceptionUtils.getRootCauseMessage(exception));
            return retVo;
        }
    }


    /**
     * 此接口由后端软件分析器，对特殊类型的文件上传调用
     * 第一步，由前端上传文件后（newOrder）接口，检查到文件类型是（cadFile）发送消息通知后端软件，转换后调用上传
     * 1.由本应该将文件上传到指定位置
     * 2.调用BILL 服务，创建上传文件的日志记录，并把文件日志记录ID，回写到模型记录的（stp_file)字段
     * 3.往 topic_task 队列发送消息通知
     * 第二步，合并回第一步（newOrder） 后续的逻辑
     *
     * @param file
     * @param modelId
     * @return
     */
    @PostMapping("/upFile/{modelId}")
    public AjaxResultVO upFileSoftConvert(@RequestParam(name = "file", required = true) MultipartFile file, @PathVariable(name = "modelId", required = true) Long modelId) {
        log.info("modelId:{}", modelId);
        log.info("file:{}", file);
        AjaxResultVO ajaxResultVO = null;
        try {
            if (file == null) {
                throw new BusinessException("文件不能为空");
            }
            if (modelId == null) {
                throw new BusinessException("modelId不能为空");
            }
            Result<FileUpLogVo> result = fileService.upLoadOrderModel(file);
            if (!result.isSuccess()) {
                ajaxResultVO = AjaxResultHelper.error();
                ajaxResultVO.setMsg("SoftConvert 创建订单失败，原因上传文件失败：-" + result.getDescription());
                return ajaxResultVO;
            }
            String lot = DateUtil.format(new Date(), "yyyyMMddHHmmss");
            FileUpLogVo upLogEntity = result.getData();
            upLogEntity.setUpLot(lot);
            upLogEntity.setCreatedTime(null);
            upLogEntity.setSource("SoftConvert");
            com.tuzhi.base.web.vo.Result<Long> resultVO = billApiClient.createUploadLogAndUpdateModel(upLogEntity, modelId);
            log.info("SoftConvert createUploadLogAndUpdateModel ：{}", resultVO != null ? new JSONObject(resultVO).toString() : "返回空");
            if ("0".equalsIgnoreCase(resultVO.getCode())) {
                ajaxResultVO = AjaxResultHelper.success();
                Message message = new Message(String.format(modelId.toString()).getBytes());
                msgProducter.sendMsg("topic_task", "parse.step", message);
                log.info("SoftConvert 发送消息成功！topic_task,parse.step{}", modelId);
            } else {
                ajaxResultVO = AjaxResultHelper.error();
            }
            return ajaxResultVO;

        } catch (Exception ex) {
            log.warn("SoftConvert 上传异常，orderInfo{}-{}", modelId, ExceptionUtils.getRootCauseStackTrace(ex));
            ResultConstant.RC rc = new ResultConstant.RC("uploadFilessErr", ex.getMessage());
            AjaxResultVO ajax = AjaxResultHelper.build(rc);
            return ajax;
        }
    }

    /**
     * 文件下载(通过上传文件日志ID)
     *
     * @param fileUpLogId id
     * @param request     请求
     * @param response    响应
     */
    @ApiOperation(value = "下载文件")
    @GetMapping(value = "/down4fid/{fileUpLogId}")
    public void downFile4FileUpLogId(@PathVariable("fileUpLogId") Long fileUpLogId, HttpServletRequest request, HttpServletResponse response
            , @RequestHeader(required = false) String redirect) {

        log.info("通过上传文件日志ID下载文件.{}", fileUpLogId);
        if (fileUpLogId == null) {
            throw new BusinessException("fid 请求参数为空");
        }
        com.tuzhi.base.web.vo.Result<List<String>> result = billApiClient.getFilePath4FileUpLogId(fileUpLogId);
        log.info("result:{}", result);
        if (result == null && !result.getCode().equals("0")) {
            throw new BusinessException("获取路径失败");
        }
        List<String> paths = result.getData();
        if (paths == null || paths.isEmpty()) {
            throw new BusinessException("获取路径失败");
        }
        String filePath = Paths.get(fileStoreConfig.getSavePath(), paths.toArray(new String[0])).toString();
        boolean fileExist = FileUtil.exist(filePath);
        if (!fileExist) {
            boolean isRedirect = fileStoreConfig.redirectDownFile(redirect);//是否可以跳转到其他服务器下载
            if (isRedirect) {
                //文件不存在，不是跳转过来的请求，则跳转到另一个服务器
                log.info("文件下载(通过上传文件日志ID),fileUpLogId={}，跳转下载", fileUpLogId);
                InputStream inputStream = fileStoreOtherApi.downFile4FileUpLogId(fileUpLogId);
                TZFileUtils.downFile(filePath, null, request, response, inputStream);
            } else {
                throw new BusinessException(String.format("文件不存在：%s", filePath));
            }
        } else {
            TZFileUtils.downFile(filePath, null, request, response, false);
        }
    }

    @PostMapping({"/updateCustomer4App"})
    public ResultBO updateCustomerNotCheckErp(@RequestBody String updateCustomerVo, @RequestHeader(value = "Authorization", required = false) String token) {
        ResultBO resultVO = billApiClient.updateCustomerNotCheckErp(updateCustomerVo, token);
        return resultVO;
    }

    @GetMapping({"/test"})
    public AjaxResultVO test() {
        log.info("心跳接口测试，接口正常");
        AjaxResultVO ajaxResultVO = AjaxResultHelper.success();
        ajaxResultVO.setMsg("心跳接口测试，接口正常:");
        ajaxResultVO.setData(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
        return ajaxResultVO;
    }
}
